/*****************************************************************************
Copyright 2006 Kervin L. Pierre
    EMAIL: otlkcon@adevsoft.com

This file is part of Otlkcon.

Otlkcon is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

Otlkcon is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Lesser GNU General Public License for more details.

You should have received a copy of the Lesser GNU General Public License
along with Otlkcon; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*******************************************************************************/

#include "StdAfx.h"
#include "O_IFolder.h"
#include "otlkcon_generic_msgstore.h"

O_IFolder::O_IFolder(void)
{
    className = _strdup("O_IFolder");

    assocContentsTable = NULL;
	parent = NULL;
	bRootFolder = FALSE;
}

O_IFolder::~O_IFolder(void)
{
	if(NULL != parent)
	{
		memObj->free(parent);
		parent = NULL;
	} // if  
	OTLKCON_DEBUGBREAK;
}

HRESULT O_IFolder::Create( otlkcon_generic_msgstore *mstr, MAPIUID ig, char *name, 
                                                    O_IFolder *parentFldr )
{
    HRESULT result = S_OK;
    int propCount = 0;
    SPropValue defaultProps[16] = {0};

    msgstore = mstr;

    if( parent == NULL )
	{
        parent = entryID;
	}

    result = O_IContainer::Create( msgstore, ig, otlkcon_imapifolder_guid );
    if( result != S_OK )
    {
        OTLKCON_DEBUGBREAK
        return result;
    }

    entryID->supportedInterfaces |= OTLKCON_IMAPIFOLDER_CLASS;

	parent = (O_ENTRYID*)memObj->malloc( sizeof(O_ENTRYID) );
	memcpy(parent, parentFldr?parentFldr->entryID:entryID, sizeof(O_ENTRYID));
	if(NULL == parentFldr) 
	{
		bRootFolder = TRUE;
	} // if

	char *str1 = (char *)calloc(1, 100);
    EntryID2String(entryID, &str1);
    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL,
        L"O_IFolder::Create() for '%S'\n    ID1=%S\n", name, str1);
    free(str1);

    //Set a few default properties
    
    //PR_OBJECT_TYPE
    //The primary interface of this object
    defaultProps[propCount].ulPropTag = PR_OBJECT_TYPE;
    defaultProps[propCount++].Value.l   =   MAPI_FOLDER;

    //PR_STORE_SUPPORT_MASK
    //Bitmask of capabilites for MAPI clients
    defaultProps[propCount].ulPropTag = PR_STORE_SUPPORT_MASK;
    defaultProps[propCount++].Value.l   =     STORE_CREATE_OK
                                        | STORE_ENTRYID_UNIQUE 
                                        | STORE_MODIFY_OK
                                        | STORE_RESTRICTION_OK
                                        | STORE_SORT_OK
                                        | STORE_SEARCH_OK 
                                        | STORE_NOTIFY_OK 
                                        | STORE_SUBMIT_OK ;

    //PR_ENTRYID
    entryID->typeID = MAPI_FOLDER;
    entryID->primarySupportedInterface = OTLKCON_IMAPIFOLDER_CLASS;

    defaultProps[propCount].ulPropTag = PR_ENTRYID;
    defaultProps[propCount].Value.bin.lpb = (LPBYTE)entryID;
    defaultProps[propCount++].Value.bin.cb  = sizeof(O_ENTRYID);

    defaultProps[propCount].ulPropTag = PR_FOLDER_TYPE;
    defaultProps[propCount++].Value.l = FOLDER_GENERIC;

    defaultProps[propCount].ulPropTag = PR_RECORD_KEY;
    defaultProps[propCount].Value.bin.lpb = (LPBYTE)entryID;
    defaultProps[propCount++].Value.bin.cb  = sizeof(O_ENTRYID);

    defaultProps[propCount].ulPropTag = PR_ACCESS;
    defaultProps[propCount++].Value.l = MAPI_ACCESS_CREATE_CONTENTS | 
            MAPI_ACCESS_CREATE_ASSOCIATED |
            MAPI_ACCESS_CREATE_HIERARCHY | 
            MAPI_ACCESS_DELETE | MAPI_ACCESS_MODIFY | MAPI_ACCESS_READ;

    defaultProps[propCount].ulPropTag  = PR_ACCESS_LEVEL;
    defaultProps[propCount++].Value.l = MAPI_MODIFY;

    defaultProps[propCount].ulPropTag  = PR_DISPLAY_NAME;
    defaultProps[propCount++].Value.lpszA = _strdup(name);

    defaultProps[propCount].ulPropTag = PR_PARENT_ENTRYID;
    defaultProps[propCount].Value.bin.lpb= (LPBYTE)(parent);
    defaultProps[propCount++].Value.bin.cb  = sizeof(O_ENTRYID);

    defaultProps[propCount].ulPropTag = PR_CONTENT_COUNT;
    defaultProps[propCount++].Value.l = 0;
	
	defaultProps[propCount].ulPropTag = PR_ASSOC_CONTENT_COUNT;
	defaultProps[propCount++].Value.l = 0;

    defaultProps[propCount].ulPropTag = PR_CONTENT_UNREAD;
    defaultProps[propCount++].Value.l = 0;

    defaultProps[propCount].ulPropTag = PR_SUBFOLDERS;
    defaultProps[propCount++].Value.l = FALSE;

    // Default class.  Should be replaced per object if necessary.
	//defaultProps[propCount].ulPropTag = PR_CONTAINER_CLASS;
    //defaultProps[propCount++].Value.lpszA = memObj->_strdup("IPM.Note");

    result = O_IProp::_IntSetProps(propCount, (LPSPropValue)&defaultProps, NULL );
    if( result != S_OK )
    {
        OTLKCON_DEBUGBREAK;
    }

    // Create associated contents table
    SizedSPropTagArray(30, contProps);

    propCount = 0;
    contProps.aulPropTag[propCount++] = PR_INSTANCE_KEY;
    contProps.aulPropTag[propCount++] = PR_ACCESS_LEVEL;
    contProps.aulPropTag[propCount++] = PR_PRIORITY;
    contProps.aulPropTag[propCount++] = PR_DISPLAY_CC;
    contProps.aulPropTag[propCount++] = PR_DISPLAY_TO;
    contProps.aulPropTag[propCount++] = PR_ENTRYID;
    contProps.aulPropTag[propCount++] = PR_HASATTACH;
    contProps.aulPropTag[propCount++] = PR_LAST_MODIFICATION_TIME;
    contProps.aulPropTag[propCount++] = PR_MAPPING_SIGNATURE;
    contProps.aulPropTag[propCount++] = PR_MESSAGE_CLASS;
    contProps.aulPropTag[propCount++] = PR_MESSAGE_FLAGS;
    contProps.aulPropTag[propCount++] = PR_MESSAGE_SIZE;
    contProps.aulPropTag[propCount++] = PR_MSG_STATUS;
    contProps.aulPropTag[propCount++] = PR_OBJECT_TYPE;
    contProps.aulPropTag[propCount++] = PR_PARENT_ENTRYID;
    contProps.aulPropTag[propCount++] = PR_RECORD_KEY;
    contProps.aulPropTag[propCount++] = PR_STORE_ENTRYID;
    contProps.aulPropTag[propCount++] = PR_STORE_RECORD_KEY;
    contProps.aulPropTag[propCount++] = PR_CONTENT_COUNT;
    contProps.aulPropTag[propCount++] = PR_SUBJECT;
    contProps.aulPropTag[propCount++] = PR_NULL;
    contProps.aulPropTag[propCount++] = PR_NULL;
    contProps.aulPropTag[propCount++] = PR_NULL;
    contProps.aulPropTag[propCount++] = PR_NULL;
    contProps.aulPropTag[propCount++] = PR_NULL;
    contProps.aulPropTag[propCount++] = PR_NULL;
    contProps.aulPropTag[propCount++] = PR_NULL;
    contProps.aulPropTag[propCount++] = PR_NULL;
    contProps.aulPropTag[propCount++] = PR_NULL;
    contProps.aulPropTag[propCount++] = PR_NULL;

    contProps.cValues = propCount;

    //PR_FOLDER_ASSOCIATED_CONTENTS
    result = OpenProperty(PR_FOLDER_ASSOCIATED_CONTENTS, &IID_IMAPITable, 0, 
                    MAPI_CREATE|MAPI_MODIFY, (LPUNKNOWN *)&assocContentsTable);
    if( result != S_OK )
    {
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
            L"O_IFolder::Create() failed :  CreateTable() failed");
        OTLKCON_DEBUGBREAK
        return E_FAIL;
    }
    assocContentsTable->derefAssocObj = TRUE;
    result = assocContentsTable->Init( (LPSPropTagArray)&contProps );
    if( result != S_OK )
    {
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
            L"O_IFolder::Create() failed :  CreateTable() failed");
        OTLKCON_DEBUGBREAK
        return E_FAIL;
    }
    assocContentsTable->objName = memObj->_strdup("associatedContents");

    objName = _strdup(name);

    // Notify listeners
    LPNOTIFICATION notif = (LPNOTIFICATION)memObj->malloc( sizeof(NOTIFICATION) );
    notif->ulEventType = fnevObjectCreated;
    notif->info.obj.cbEntryID = sizeof(O_ENTRYID); //FIXME: Wrapped or not?
    notif->info.obj.lpEntryID = (LPENTRYID)entryID;
    notif->info.obj.ulObjType = MAPI_FOLDER;
    notif->info.obj.cbParentID = sizeof(O_ENTRYID);
    notif->info.obj.lpParentID = (LPENTRYID)(parent);

    // Notify any listener interested in our parents that the
    // parent has a new child.
	ULONG ulNotifyFlags = 0;
    result = msgstore->Notify(parent, 1, notif, &ulNotifyFlags);
 
    // Folder should also listen for object creation on itself. Eg. for
    // updating the contents table.
    result = msgstore->InternalAdvise( sizeof(O_ENTRYID), (LPENTRYID)entryID, 
        fnevObjectCreated|fnevObjectModified|fnevObjectDeleted, adviseSink,
        NOTIFY_SYNC, &objectCreateConnection);

//    // Listen for subfolder creation on this object
//    // Ie.  Make sure parent folders listen for events on their child folders
//    result = msgstore->Advise( sizeof(O_ENTRYID), (LPENTRYID)entryID, 
//        fnevObjectCreated, parent->adviseSink, &objectCreateConnection);

    return result;
}

HRESULT O_IFolder::Load( otlkcon_generic_msgstore *msgstr, O_ENTRYID *eid )
{
    HRESULT result = S_OK;
    ULONG count;
    LPSPropValue propResult = 0;

    // First thing...
    // Make sure this is a entryid for a folder object
    if( memcmp(&(eid->typeGuid),&otlkcon_imapifolder_guid,sizeof(MAPIUID)) )
    {
        OTLKCON_DEBUGBREAK
        return MAPI_E_INVALID_PARAMETER;
    }

    msgstore = msgstr;
    result = O_IContainer::Load( msgstore, eid  );
    if( result != S_OK )
    {
        return result;
    }

    SizedSPropTagArray(3,tags);
    tags.cValues       = 3;
    tags.aulPropTag[0] = PR_ENTRYID;
    tags.aulPropTag[1] = PR_DISPLAY_NAME;
	tags.aulPropTag[2] = PR_PARENT_ENTRYID;

	result = O_IProp::_IntGetProps((LPSPropTagArray)&tags, 0, &count, &propResult);
	switch( result )
	{
		case MAPI_W_ERRORS_RETURNED:
		case S_OK:			
            for( int i=0; i<3; i++ )
            {
                if( PROP_ID(propResult[i].ulPropTag) == PROP_ID(PR_ENTRYID) )
                {
                    if( PROP_TYPE(propResult[i].ulPropTag) == PT_BINARY )
                    {
                        entryID = (O_ENTRYID*)(propResult[i].Value.bin.lpb);
                    }
                    else
                    {
                        // Probably PT_ERROR
                        OTLKCON_DEBUGBREAK;
                    }	    
                }
                else if( PROP_ID(propResult[i].ulPropTag) == PROP_ID(PR_DISPLAY_NAME) )
                {
                    if( PROP_TYPE(propResult[i].ulPropTag) == PT_STRING8 )
                    {
                        objName = _strdup(propResult[i].Value.lpszA);
                    }
                    else
                    {
                        // Probably PT_ERROR
                        OTLKCON_DEBUGBREAK;
                    }
                }
				else if( PROP_ID(propResult[i].ulPropTag) == PROP_ID(PR_PARENT_ENTRYID) )
				{
					if( PROP_TYPE(propResult[i].ulPropTag) == PT_BINARY )
					{
						parent = (O_ENTRYID*)memObj->malloc( sizeof(O_ENTRYID) );
						memcpy(parent, propResult[i].Value.bin.lpb, sizeof(O_ENTRYID));
					}
					else
					{
						// Probably PT_ERROR
						OTLKCON_DEBUGBREAK;
					}
				}
                else
                {
                    // Unknown property
                    OTLKCON_DEBUGBREAK;
                }
            }

			break;

		default:
			//Error condition
            OTLKCON_DEBUGBREAK;
			break;
    }

	if(0 == memcmp(parent, entryID, sizeof(O_ENTRYID))) 
	{
		bRootFolder = TRUE;
	} // if

    result = OpenProperty(PR_FOLDER_ASSOCIATED_CONTENTS, &IID_IMAPITable, 0, 
                    MAPI_MODIFY, (LPUNKNOWN *)&assocContentsTable);
    contentsTable->objName = memObj->_strdup("associatedContents");

	if(SUCCEEDED(result))
	{
		// Folder should also listen for object creation on itself. Eg. for
		// updating the contents table.
		result = msgstore->InternalAdvise( sizeof(O_ENTRYID), (LPENTRYID)entryID,
			fnevObjectCreated|fnevObjectModified|fnevObjectDeleted, 
            adviseSink, NOTIFY_SYNC, &objectCreateConnection);
	} // if

    return result;
}

void O_IFolder::Destroy()
{
    ;
}

STDMETHODIMP O_IFolder::CreateMessage(LPCIID lpInterface,
  ULONG ulFlags,
  LPMESSAGE *lppMessage  
)
{
    HRESULT result = S_OK;
    O_IMessage *ret;

    //Validate_IMAPIFolder_CreateMessage(this,lpInterface,ulFlags,lppMessage);

    // TODO: Find out what to do with lpInterface

    ret = new O_IMessage();
    result = ret->Create(msgstore, entryID, 
                                (ulFlags&MAPI_ASSOCIATED)?MSGFLAG_ASSOCIATED:0,
                                otlkcon_null_guid    );
    if( result != S_OK )
    {
        OTLKCON_DEBUGBREAK;
        result = E_FAIL;
        goto createmessage_end;
    }

    *lppMessage = (LPMESSAGE)ret;

//    // Listen for ObjectModified notifications on this
//    result = msgstore->Advise( sizeof(O_ENTRYID), (LPENTRYID)ret->entryID, 
//        fnevObjectCreated, adviseSink, &objectCreateConnection);

/*
	//Henry 30.11.2005:
	//TODO
	//We need to make it in the O_IMessage::SaveChanges
	//also we need to check message flags for ordinary messages 
	//and if message's read state have changed modify
	//PR_CONTENT_UNREAD
	
	SizedSPropTagArray(1, taContents);
	
	taContents.cValues = 1;
	taContents.aulPropTag[0] = (ulFlags&MAPI_ASSOCIATED)?PR_ASSOC_CONTENT_COUNT:PR_CONTENT_COUNT;

	SPropValue pvFirstMessage = {0};
	pvFirstMessage.ulPropTag = taContents.aulPropTag[0];
	pvFirstMessage.Value.l = 1;

	LPSPropValue lpContentCount = NULL;
	ULONG cValues = 1;
	result = _IntGetProps((LPSPropTagArray)&taContents, 0, &cValues, &lpContentCount);
	if(SUCCEEDED(result) && NULL != lpContentCount && 
		PT_ERROR != PROP_TYPE(lpContentCount->ulPropTag))
	{
		pvFirstMessage.Value.l = lpContentCount->Value.l + 1;
		memObj->lpFreeBuffer(lpContentCount);
	} // if
	
	result = _IntSetProps(1, &pvFirstMessage, NULL);
	
	//notify about object change
	if(SUCCEEDED(result))
	{
		result = SaveChanges(KEEP_OPEN_READWRITE);
	} // if
*/

createmessage_end:
    return result;
}

STDMETHODIMP O_IFolder::CopyMessages(LPENTRYLIST lpMsgList,
  LPCIID lpInterface,
  LPVOID lpDestFolder,
  ULONG ulUIParam,
  LPMAPIPROGRESS lpProgress,
  ULONG ulFlags            
)
{
  HRESULT result = S_OK;

  //Validate_IMAPIFolder_CopyMessages(this,lpMsgList,lpInterface,lpDestFolder,
    //            ulUIParam,lpProgress,ulFlags);

  result = msgstore->lpMAPISup->CopyMessages( &IID_IMAPIFolder, this,
                 lpMsgList, lpInterface, lpDestFolder, NULL, lpProgress, ulFlags);

  // FIXME:  Shouldn't we throw 'fnevObjectCopied' Notification for listners?

  return result;
}

STDMETHODIMP O_IFolder::DeleteMessages(LPENTRYLIST lpMsgList,
  ULONG ulUIParam,
  LPMAPIPROGRESS lpProgress,
  ULONG ulFlags            
)
{
    HRESULT result = S_OK;
    HRESULT finalResult = S_OK;
    SRestriction rt = {0};
    SPropValue prop = {0};

    //  Validate_IMAPIFolder_DeleteMessages(this,lpMsgList,ulUIParam,lpProgress,
    //              ulFlags);

    for( ULONG i = 0; i < lpMsgList->cValues; i++ )
    {
        prop.ulPropTag  = PR_ENTRYID;
        prop.Value.bin = lpMsgList->lpbin[i];
        rt.rt = RES_PROPERTY;
        rt.res.resProperty.ulPropTag = PR_ENTRYID;
        rt.res.resProperty.relop     = RELOP_EQ;
        rt.res.resProperty.lpProp    = &prop;

        // FIXME: Shouldn't we search associates as well?
        result = contentsTable->FindRow(&rt, BOOKMARK_BEGINNING, 0);
        switch(result)
        {
            case S_OK:
            case MAPI_W_POSITION_CHANGED:
                //Message found
                break;

            default:
                // The object is not in the contents table
                if( finalResult == S_OK )
                {
                    finalResult = MAPI_W_PARTIAL_COMPLETION;
                    continue;
                }
                break;
        }

        if( isForeignView)
        {
            // Foreign folder view that's not ready.
            // Mark delete pending
            O_IMessage *msg = NULL;
            ULONG objType = 0;
            result = msgstore->OpenEntry(sizeof(O_ENTRYID), 
                (LPENTRYID)(lpMsgList->lpbin[i].lpb), 
                &IID_IMessage, 0, &objType, (LPUNKNOWN*)&msg);
            if( result != S_OK )
            {
                OTLKCON_DEBUGBREAK;
                continue;
            }

            result = DeleteRemoteMessage(msg,OTLKCON_ACTION_DEL);
            if( result != S_OK )
            {
                OTLKCON_DEBUGBREAK;
                continue;
            }
			if(xpAccountEID != NULL )
			{
				//delete request is sent to the transport
				//so we can delete object
				// Remove the object
				HRESULT res=0;
				result = msgstore->ObjectExists( msg->entryID, &res );
				if( res )
				{
					result = msgstore->RemoveObject( msg->entryID, entryID );
					if( result != S_OK )
					{
						OTLKCON_DEBUGBREAK;
					}
				}
			} // if
//            result = contentsTable->HrUpdateObjectRow(0, msg);
//            if( result != S_OK )
//            {
//                OTLKCON_DEBUGBREAK;
//                continue;
//            }

            continue;
        }
        else
        {
            // Finally, delete the current object
            result = msgstore->RemoveObject( 
                            (O_ENTRYID *)(lpMsgList->lpbin[i].lpb), entryID );
            if( result != S_OK )
            {
                // Faile removing object.
                if( finalResult == S_OK )
                {
                    finalResult = MAPI_W_PARTIAL_COMPLETION;
                }
                continue;
            }
        }
    }

    return finalResult;
}

STDMETHODIMP O_IFolder::CreateFolder(ULONG ulFolderType,
  LPTSTR lpszFolderName,
  LPTSTR lpszFolderComment,
  LPCIID lpInterface,
  ULONG ulFlags,
  LPMAPIFOLDER *lppFolder  
)
{
    HRESULT result = S_OK;
    O_IFolder *child = NULL;
    LPSRowSet rowset = NULL;
    SRow row = {0};
    SPropValue prop = {0};
    ULONG propCount = 0;

    Validate_IMAPIFolder_CreateFolder(this,ulFolderType,lpszFolderName,
        lpszFolderComment,lpInterface,ulFlags,lppFolder);

    lastCalledFunctionID = OTLKCON_IFOLDER_CREATEFOLDER;
    strcpy(lastCalledFunctionName, OTLKCON_IFOLDER_CREATEFOLDER_STR);
    SetLastError(S_OK, "");

    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
        L"inside O_IFolder::CreateFolder() creating '%S'.\n", lpszFolderName);

    if( lpInterface != NULL 
            && memcmp( lpInterface, &IID_IMAPIFolder, sizeof(IID)) )
    {
        //We only support IMAPIFolder interface
        OTLKCON_DEBUGBREAK
        return E_FAIL;
    }

    if( lpszFolderName == NULL )
    {
        OTLKCON_DEBUGBREAK
        SetLastError(MAPI_E_INVALID_PARAMETER, "Folder name cannot be empty.");
        return MAPI_E_INVALID_PARAMETER;
    }

    //Search the hierarchy table for folder name
    SRestriction rt = {0};
    rt.rt = RES_PROPERTY;
    SContentRestriction cr = {0};

    prop.ulPropTag  = PR_DISPLAY_NAME;
    prop.Value.lpszA = lpszFolderName;
    cr.lpProp = &prop;
    cr.ulFuzzyLevel = FL_FULLSTRING | FL_IGNORECASE;
    cr.ulPropTag = PR_DISPLAY_NAME;
    rt.res.resContent = cr;

    //FIXME: Either garantee that either IMAPIFolder object in memory
    //     knows about each other, or always pull the hierarchy table
    //     from disk.  Even then there is the possibility of races.
    result = hierarchyTable->FindRow(&rt, BOOKMARK_BEGINNING, 0);
    switch(result)
    {
        case S_OK:
        case MAPI_W_POSITION_CHANGED:
            //Folder found
            if( ulFlags & OPEN_IF_EXISTS )
            {
                //Open the folder
                OTLKCON_DEBUGBREAK;
            }
            else
            {
                return MAPI_E_COLLISION;
            }
            break;

        case MAPI_E_NOT_FOUND:
            //Folder was not found
            break;

        default:
            OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL,
                L"IMAPIFolder::CreateFolder() : hierarchyTable->FindRow returned %X",
                result );
            OTLKCON_DEBUGBREAK;
            break;
    }

    //Add the folder to the hierarchy table
    child = new O_IFolder();
    MAPIUID mu = {0};
    result = child->Create( msgstore, mu, lpszFolderName, this );
    if( result != S_OK )
    {
        OTLKCON_DEBUGBREAK;
        return result;
    }

    // We need a few properties from the parent before we build
    // the hierarchy table
    SizedSPropTagArray(4,tags);
    tags.cValues       = 4;
    tags.aulPropTag[0] = PR_ACCESS;
    tags.aulPropTag[1] = PR_STORE_ENTRYID;
    tags.aulPropTag[2] = PR_STORE_RECORD_KEY;
    tags.aulPropTag[3] = PR_MAPPING_SIGNATURE;
    
    LPSPropValue propResult;
    LPENTRYID storeEntryID = NULL;
    O_ENTRYID *storeMappingSig = NULL;
    int storeEntryIDSize = 0, storeRecordKeySize = 0, storeMappingSigSize = 0;
    ULONG access = 0;
    LPMAPIUID storeRecordKey = NULL;

	result = O_IProp::GetProps((LPSPropTagArray)&tags, 0, &propCount, &propResult);
	switch( result )
	{
		case MAPI_W_ERRORS_RETURNED:
		case S_OK:			
            for( int i=0; i<4; i++ )
            {
                if( PROP_ID(propResult[i].ulPropTag) == PROP_ID(PR_STORE_ENTRYID) )
                {
                    if( PROP_TYPE(propResult[i].ulPropTag) == PT_BINARY )
                    {
                        storeEntryID = (LPENTRYID)(propResult[i].Value.bin.lpb);
                        storeEntryIDSize = propResult[i].Value.bin.cb;
                    }
                    else
                    {
                        // Probably PT_ERROR
                        OTLKCON_DEBUGBREAK;
                    }	    
                }
                else if( PROP_ID(propResult[i].ulPropTag) == PROP_ID(PR_ACCESS) )
                {
                    if( PROP_TYPE(propResult[i].ulPropTag) == PT_LONG )
                    {
                        access = propResult[i].Value.l;
                    }
                    else
                    {
                        // Probably PT_ERROR
                        OTLKCON_DEBUGBREAK;
                    }
                }
                else if( PROP_ID(propResult[i].ulPropTag) == PROP_ID(PR_STORE_RECORD_KEY) )
                {
                    if( PROP_TYPE(propResult[i].ulPropTag) == PT_BINARY )
                    {
                        storeRecordKey = (LPMAPIUID)(propResult[i].Value.bin.lpb);
                        storeRecordKeySize = propResult[i].Value.bin.cb;
                    }
                    else
                    {
                        // Probably PT_ERROR
                        OTLKCON_DEBUGBREAK;
                    }	    
                }
                else if( PROP_ID(propResult[i].ulPropTag) == PROP_ID(PR_MAPPING_SIGNATURE) )
                {
                    if( PROP_TYPE(propResult[i].ulPropTag) == PT_BINARY )
                    {
                        storeMappingSig = (O_ENTRYID *)(propResult[i].Value.bin.lpb);
                        storeMappingSigSize = propResult[i].Value.bin.cb;
                    }
                    else
                    {
                        // Probably PT_ERROR
                        OTLKCON_DEBUGBREAK;
                    }	    
                }
                else
                {
                    // Unknown property
                    OTLKCON_DEBUGBREAK;
                }
            }

			break;

		default:
			//Error condition
            OTLKCON_DEBUGBREAK;
			break;
    }

    
	//Henry 1.12.2005
	//We have only place for update tables
	result = hierarchyTable->HrUpdateObjectRow(0, child);

//	//Build the hierarchy table row
//    SPropValue props[20] = {0};
//    propCount = 0;
//    props[propCount].ulPropTag = PR_INSTANCE_KEY;
//    props[propCount].Value.bin.lpb = (LPBYTE)&(child->entryID->instanceGuid);
//    props[propCount++].Value.bin.cb  = sizeof(MAPIUID);
//    props[propCount].ulPropTag = PR_COMMENT;
//    props[propCount++].Value.lpszA = lpszFolderComment;
//    props[propCount].ulPropTag = PR_DISPLAY_NAME;
//    props[propCount++].Value.lpszA = lpszFolderName;
//    props[propCount].ulPropTag = PR_OBJECT_TYPE;
//    props[propCount++].Value.l = MAPI_FOLDER;
//    props[propCount].ulPropTag = PR_DISPLAY_TYPE;
//    props[propCount++].Value.l = DT_FOLDER;
//    props[propCount].ulPropTag = PR_DEPTH;
//    props[propCount++].Value.l = 1;
//    props[propCount].ulPropTag = PR_STATUS;
//    props[propCount++].Value.l = 0;
//    props[propCount].ulPropTag = PR_FOLDER_TYPE;
//    props[propCount++].Value.l = FOLDER_GENERIC;
//    props[propCount].ulPropTag = PR_SUBFOLDERS;
//    props[propCount++].Value.l = FALSE;
//    props[propCount].ulPropTag = PR_CONTENT_COUNT;
//    props[propCount++].Value.l = 0;
//    
//    props[propCount].ulPropTag = PR_CONTENT_UNREAD;
//    props[propCount++].Value.l = 0;
//    props[propCount].ulPropTag = PR_ENTRYID;
//    props[propCount].Value.bin.lpb = (LPBYTE)child->entryID;
//    props[propCount++].Value.bin.cb  = sizeof(O_ENTRYID);
//    props[propCount].ulPropTag = PR_ACCESS;
//    props[propCount++].Value.l =  access;
//    props[propCount].ulPropTag = PR_PARENT_ENTRYID;
//    props[propCount].Value.bin.lpb = (LPBYTE)entryID;
//    props[propCount++].Value.bin.cb  = sizeof(O_ENTRYID);
//    props[propCount].ulPropTag = PR_STORE_ENTRYID;
//    props[propCount].Value.bin.lpb = (LPBYTE)storeEntryID;
//    props[propCount++].Value.bin.cb  = storeEntryIDSize;
//    props[propCount].ulPropTag = PR_STORE_RECORD_KEY;
//    props[propCount].Value.bin.lpb = (LPBYTE)storeRecordKey;
//    props[propCount++].Value.bin.cb  = storeRecordKeySize;
//    props[propCount].ulPropTag = PR_MAPPING_SIGNATURE;
//    props[propCount].Value.bin.lpb = (LPBYTE)storeMappingSig;
//    props[propCount++].Value.bin.cb  = storeMappingSigSize;
//    props[propCount].ulPropTag = PR_RECORD_KEY;
//    props[propCount].Value.bin.lpb = (LPBYTE)child->entryID;
//    props[propCount++].Value.bin.cb  = sizeof(O_ENTRYID);
//    //props[propCount].ulPropTag = PR_CONTAINER_CLASS;
//    //props[propCount++].Value.lpszA = memObj->_strdup("IPM.Note");
//    row.cValues = propCount;
//    row.lpProps = (LPSPropValue)&props;
//    result = hierarchyTable->HrModifyRow( &row );

   // result = hierarchyTable->Dump( OTLKCON_DUMP_XML_INCLUDE_ROOT );

    // Set PR_SUBFOLDERS to TRUE if it's FALSE
    prop.ulPropTag = PR_SUBFOLDERS;
    prop.Value.b = TRUE;
    result = _IntSetProps(1, &prop, NULL);

    if(SUCCEEDED(result))
    {
    	//Notify and update parent hierarchy table
		SaveChanges(KEEP_OPEN_READWRITE);
    } // if
	// Set a 
    *lppFolder = (LPMAPIFOLDER)child;

    return result;
}

STDMETHODIMP O_IFolder::CopyFolder(ULONG cbEntryID,
  LPENTRYID lpEntryID,
  LPCIID lpInterface,
  LPVOID lpDestFolder,
  LPTSTR lpszNewFolderName,
  ULONG ulUIParam,
  LPMAPIPROGRESS lpProgress,
  ULONG ulFlags               
)
{
    HRESULT result = S_OK;
    O_IFolder *destSubFolder = NULL;
    O_IFolder *subFolder = NULL;
    ULONG objType;
    ULONG dialogFlag, moveFlag;

    // Validate_IMAPIFolder_CopyFolder(this,cbEntryID,lpEntryID,lpInterface,
    //    lpDestFolder,lpszNewFolderName,ulUIParam,lpProgress,ulFlags);

    // FIXME: Most error paths are being ignore!
    dialogFlag = (ulFlags & FOLDER_DIALOG )?FOLDER_DIALOG:0;
    moveFlag = (ulFlags & FOLDER_MOVE )?FOLDER_MOVE:0;

    // Open the subfolder if it's alright.
    result = OpenEntry( cbEntryID, lpEntryID, &IID_IMAPIFolder, 0, &objType,
                            (LPUNKNOWN *)&subFolder );
    if( result != S_OK )
    {
        OTLKCON_DEBUGBREAK;
    }

    // Create the destination subfolder
    result = ((O_IFolder *)lpDestFolder)->CreateFolder(FOLDER_GENERIC,lpszNewFolderName,
            "",NULL, 0, (LPMAPIFOLDER *)&destSubFolder);
    if( result != S_OK )
    {
        OTLKCON_DEBUGBREAK;
    }

    // Do the property copy
    result = subFolder->CopyTo(0,NULL,NULL,ulUIParam,lpProgress,&IID_IMAPIFolder,
                                destSubFolder, 
                                ((ulFlags&MAPI_DECLINE_OK)?MAPI_DECLINE_OK:0)
                                | (dialogFlag?MAPI_DIALOG:0)
                                | (moveFlag?MAPI_MOVE:0), NULL );

    // Copy the subfolder's subfolders?
    if( ulFlags & COPY_SUBFOLDERS )
    {
        ULONG ulRows = 0;
        LPSPropTagArray currCols = NULL;
        LPSRowSet rows = NULL;
        ULONG ulEid;
        LPENTRYID eid = NULL;
        char *tmpName = NULL;

        SizedSPropTagArray( 2, rowTags );

        rowTags.cValues    = 2;
        rowTags.aulPropTag[0] = PR_ENTRYID;
        rowTags.aulPropTag[1] = PR_DISPLAY_NAME;

        // TODO: Walk the hierarchy table, do the same
        //       for each child
        result = subFolder->hierarchyTable->SeekRow( BOOKMARK_BEGINNING, 0, NULL );
        result = subFolder->hierarchyTable->QueryColumns( 0, & currCols );
        result = subFolder->hierarchyTable->SetColumns( (LPSPropTagArray)&rowTags, 0 );
        result = subFolder->hierarchyTable->GetRowCount( 0, &ulRows );
        result = subFolder->hierarchyTable->QueryRows(ulRows, TBL_NOADVANCE, &rows );
        result = subFolder->hierarchyTable->SetColumns( currCols, 0 );

        for( ULONG i = 0; i< rows->cRows; i++ )
        {
            ulEid = rows->aRow[i].lpProps[0].Value.bin.cb;
            eid   = (LPENTRYID)rows->aRow[i].lpProps[0].Value.bin.lpb;
            tmpName = rows->aRow[i].lpProps[1].Value.lpszA;

            result = subFolder->CopyFolder( ulEid, eid, lpInterface, destSubFolder,
                                            tmpName, ulUIParam, lpProgress, ulFlags);
            if( result != S_OK )
            {
                OTLKCON_DEBUGBREAK;
            }
        }     
    
		if(NULL != rows)
		{
			FreeProws(rows);
			rows = NULL;
		} // if
	}

    // Delete the source folder if we have too.
    if( moveFlag )
    {
        // TODO:  Move to wastebasket first?  Or have a
        //        _IntDeleteFolder() that deletes from any
        //        folder?
        result = DeleteFolder( cbEntryID, lpEntryID, ulUIParam, 
                            lpProgress, DEL_FOLDERS|DEL_MESSAGES
                            | dialogFlag );
        if( result != S_OK )
        {
            OTLKCON_DEBUGBREAK;
        }
    }
    return result;
}

STDMETHODIMP O_IFolder::DeleteFolder(ULONG cbEntryID,
  LPENTRYID lpEntryID,
  ULONG ulUIParam,
  LPMAPIPROGRESS lpProgress,
  ULONG ulFlags                
)
{
    HRESULT result = S_OK;
    O_IFolder *subFolder = NULL;
    ULONG ulFolderRows = 0, ulMessageRows;
    LPSPropTagArray currCols = NULL;
    LPSRowSet rows = NULL;
    ULONG ulEid,objType;
    LPENTRYID eid = NULL;
    char *tmpName = NULL;

    // Validate_IMAPIFolder_DeleteFolder(this,cbEntryID,lpEntryID,ulUIParam,
    //             lpProgress,ulFlags);

    // TODO:  Should DeleteFolder() Check for the WasteBasket
    //        entryID ?

    // FIXME: Error paths are being ignore!

    result = OpenEntry( cbEntryID, lpEntryID, &IID_IMAPIFolder, 0, &objType,
                            (LPUNKNOWN *)&subFolder );
    if( result != S_OK )
    {
        OTLKCON_DEBUGBREAK;
    }

    result = subFolder->hierarchyTable->GetRowCount( 0, &ulFolderRows );
    if( ulFolderRows > 0 && !(ulFlags & DEL_FOLDERS) )
    {
        // We have sub folders but are not instructed
        // to delete them.

        // FIXME:  Free resources

        return MAPI_E_HAS_FOLDERS;
    }

    result = subFolder->contentsTable->GetRowCount( 0, &ulMessageRows );
    if( ulMessageRows > 0 && !(ulFlags & DEL_MESSAGES) )
    {
        // We have messages but are not instructed
        // to delete them.

        // FIXME:  Free resources

        // FIXME:  Should we be checking the associated
        //         contents table?

        return MAPI_E_HAS_MESSAGES;
    }

    SizedSPropTagArray( 2, rowTags );

    // Delete messages
    if( ulMessageRows > 0 )
    {
        ENTRYLIST msgs = {0};
        rowTags.cValues    = 1;
        rowTags.aulPropTag[0] = PR_ENTRYID;

        result = subFolder->contentsTable->SeekRow( BOOKMARK_BEGINNING, 0, NULL );
        result = subFolder->contentsTable->QueryColumns( 0, & currCols );
        result = subFolder->contentsTable->SetColumns( (LPSPropTagArray)&rowTags, 0 );
        result = subFolder->contentsTable->GetRowCount( 0, &ulMessageRows );
        result = subFolder->contentsTable->QueryRows(ulMessageRows, TBL_NOADVANCE, &rows );
        result = subFolder->contentsTable->SetColumns( currCols, 0 );
        
        msgs.lpbin = (SBinary *)memObj->malloc( sizeof(SBinary)*rows->cRows );
        msgs.cValues = rows->cRows;

        for( ULONG i=0; i<rows->cRows; i++)
        {
            msgs.lpbin[i] = rows->aRow[i].lpProps[0].Value.bin;
        }

        result = DeleteMessages( &msgs, ulUIParam, lpProgress, 
                    ((ulFlags&FOLDER_DIALOG)?MESSAGE_DIALOG:0) );
    }

	if(NULL != rows)
	{
		FreeProws(rows);
		rows = NULL;
	} // if

    // Delete subfolders
    if( ulFolderRows > 0 )
    {
        rowTags.cValues    = 2;
        rowTags.aulPropTag[0] = PR_ENTRYID;
        rowTags.aulPropTag[1] = PR_DISPLAY_NAME;

        result = subFolder->hierarchyTable->SeekRow( BOOKMARK_BEGINNING, 0, NULL );
        result = subFolder->hierarchyTable->QueryColumns( 0, & currCols );
        result = subFolder->hierarchyTable->SetColumns( (LPSPropTagArray)&rowTags, 0 );
        result = subFolder->hierarchyTable->GetRowCount( 0, &ulMessageRows );
        result = subFolder->hierarchyTable->QueryRows(ulMessageRows, TBL_NOADVANCE, &rows );
        result = subFolder->hierarchyTable->SetColumns( currCols, 0 );
        for( ULONG i = 0; i< rows->cRows; i++ )
        {

            ulEid = rows->aRow[i].lpProps[0].Value.bin.cb;
            eid   = (LPENTRYID)rows->aRow[i].lpProps[0].Value.bin.lpb;
            tmpName = rows->aRow[i].lpProps[1].Value.lpszA;

            result = subFolder->DeleteFolder( ulEid, eid,  ulUIParam, lpProgress, 
                ulFlags);
            if( result != S_OK )
            {
                OTLKCON_DEBUGBREAK;
            }
        }     
    }

	if(NULL != rows)
	{
		FreeProws(rows);
		rows = NULL;
	} // if
    // Delete this subfolder
    result = msgstore->RemoveObject( subFolder->entryID, entryID );

    // Notify our listeners
    // FIXME: Is this notification being setup correctly?
    LPNOTIFICATION notif = (LPNOTIFICATION)memObj->malloc( sizeof(NOTIFICATION) );
    notif->info.obj.cbEntryID = sizeof(O_ENTRYID);
    notif->info.obj.lpEntryID = (LPENTRYID)(subFolder->entryID);
    notif->info.obj.ulObjType = MAPI_FOLDER;
    notif->ulEventType = fnevObjectDeleted;    
        
    // Notify any listener interested that this message has changed.
    ULONG ulNotifyFlags = 0;
    result = msgstore->Notify(entryID, 1, notif, &ulNotifyFlags);

    // FIXME:  We need to delete subFolder's O_ITable SQL tables

    // FIXME:  It should be safe to remove subFolder object from memory

    return S_OK;
}

STDMETHODIMP O_IFolder::SetReadFlags(LPENTRYLIST lpMsgList,
  ULONG ulUIParam,
  LPMAPIPROGRESS lpProgress,
  ULONG ulFlags            
)
{
  HRESULT result = E_FAIL;

  Validate_IMAPIFolder_SetReadFlags(this,lpMsgList,ulUIParam,lpProgress,ulFlags);

  return result;
}

STDMETHODIMP O_IFolder::GetMessageStatus(ULONG cbEntryID,
  LPENTRYID lpEntryID,
  ULONG ulFlags,
  ULONG *lpulMessageStatus  
)
{
  HRESULT result = E_FAIL;

  Validate_IMAPIFolder_GetMessageStatus(this,cbEntryID,lpEntryID,ulFlags,
            lpulMessageStatus);

  return result;
}

STDMETHODIMP O_IFolder::SetMessageStatus(ULONG cbEntryID,
  LPENTRYID lpEntryID,
  ULONG ulNewStatus,
  ULONG ulNewStatusMask,
  ULONG FAR * lpulOldStatus  
)
{
  HRESULT result = E_FAIL;

  Validate_IMAPIFolder_SetMessageStatus(this,cbEntryID,lpEntryID,ulNewStatus,
                ulNewStatusMask,lpulOldStatus);

  return result;
}

STDMETHODIMP O_IFolder::SaveContentsSort(LPSSortOrderSet lpSortCriteria,
  ULONG ulFlags                     
)
{
  HRESULT result = E_FAIL;

  Validate_IMAPIFolder_SaveContentsSort(this,lpSortCriteria,ulFlags);

  return result;
}

STDMETHODIMP O_IFolder::EmptyFolder(ULONG ulUIParam,
  LPMAPIPROGRESS lpProgress,
  ULONG ulFlags                
)
{
    HRESULT result = S_OK;
    LPSPropTagArray currCols = NULL;
    LPSRowSet rows = NULL;
    ULONG ulEid;
    LPENTRYID eid = NULL;
    char *tmpName = NULL;
    O_IFolder *subFolder = NULL;
    ULONG objType;
    ULONG ulFolderRows = 0, ulMessageRows;

    // Validate_IMAPIFolder_EmptyFolder(this,ulUIParam,lpProgress,ulFlags);

    SizedSPropTagArray( 2, rowTags );

    rowTags.cValues    = 2;
    rowTags.aulPropTag[0] = PR_ENTRYID;
    rowTags.aulPropTag[1] = PR_DISPLAY_NAME;

    // Get subfolder entryIDs
    result = hierarchyTable->SeekRow( BOOKMARK_BEGINNING, 0, NULL );
    result = hierarchyTable->QueryColumns( 0, & currCols );
    result = hierarchyTable->SetColumns( (LPSPropTagArray)&rowTags, 0 );
    result = hierarchyTable->GetRowCount( 0, &ulMessageRows );
    result = hierarchyTable->QueryRows(ulMessageRows, TBL_NOADVANCE, &rows );
    result = hierarchyTable->SetColumns( currCols, 0 );

    // Is there associated content in the subfolders?
    // FIXME: What about the current folder's associated content?
    for( ULONG i=0; i< rows->cRows; i++ )
    {
        ulEid = rows->aRow[i].lpProps[0].Value.bin.cb;
        eid   = (LPENTRYID)rows->aRow[i].lpProps[0].Value.bin.lpb;
        tmpName = rows->aRow[i].lpProps[1].Value.lpszA;

        // Open the subfolder
        result = OpenEntry( ulEid, eid, &IID_IMAPIFolder, 0, &objType,
                            (LPUNKNOWN *)&subFolder );
        if( result != S_OK )
        {
            OTLKCON_DEBUGBREAK;
        }

        result = subFolder->assocContentsTable->GetRowCount( 0, &ulMessageRows );

        if( ( ulMessageRows > 0 ) && !( ulFlags & DEL_ASSOCIATED ) )
        {
            // Error:  We have associated contents but not instructed
            //         to delete them.
            
            // TODO: Clean-up
            
            // FIXME: What's the proper return code for this
            return E_FAIL;
        }
    }

    // Delete subfolders
    for( ULONG i=0; i< rows->cRows; i++ )
    {
        ulEid = rows->aRow[i].lpProps[0].Value.bin.cb;
        eid   = (LPENTRYID)rows->aRow[i].lpProps[0].Value.bin.lpb;
        tmpName = rows->aRow[i].lpProps[1].Value.lpszA;

        result = DeleteFolder( ulEid, eid,  ulUIParam, lpProgress,
            DEL_FOLDERS|DEL_MESSAGES|((ulFlags&FOLDER_DIALOG)?FOLDER_DIALOG:0));
        if( result != S_OK )
        {
            OTLKCON_DEBUGBREAK;
        }
    }
	if(NULL != rows)
	{
		FreeProws(rows);
		rows = NULL;
	} // if

    // Delete messages
    result = contentsTable->GetRowCount( 0, &ulMessageRows );
    if( ulMessageRows > 0 )
    {
        ENTRYLIST msgs = {0};
        rowTags.cValues    = 1;
        rowTags.aulPropTag[0] = PR_ENTRYID;

        result = contentsTable->SeekRow( BOOKMARK_BEGINNING, 0, NULL );
        result = contentsTable->QueryColumns( 0, & currCols );
        result = contentsTable->SetColumns( (LPSPropTagArray)&rowTags, 0 );
        result = contentsTable->GetRowCount( 0, &ulMessageRows );
        result = contentsTable->QueryRows(ulMessageRows, TBL_NOADVANCE, &rows );
        result = contentsTable->SetColumns( currCols, 0 );
        
        msgs.lpbin = (SBinary *)memObj->malloc( sizeof(SBinary)*rows->cRows );
        msgs.cValues = rows->cRows;

        for( ULONG i=0; i<rows->cRows; i++)
        {
            msgs.lpbin[i] = rows->aRow[i].lpProps[0].Value.bin;
        }

        result = DeleteMessages( &msgs, ulUIParam, lpProgress, 
                    ((ulFlags&FOLDER_DIALOG)?MESSAGE_DIALOG:0) );
    }
	
	if(NULL != rows)
	{
		FreeProws(rows);
		rows = NULL;
	} // if

    return result;
}

//Overloaded functions
STDMETHODIMP O_IFolder::SaveChanges( ULONG ulFlags )
{
	HRESULT hResult = O_IProp::SaveChanges(ulFlags);
	if(SUCCEEDED(hResult))
	{
		// Notify listeners        
		LPNOTIFICATION notif = (LPNOTIFICATION)memObj->malloc( sizeof(NOTIFICATION) );
		notif->info.obj.cbEntryID = sizeof(O_ENTRYID);
		notif->info.obj.lpEntryID = (LPENTRYID)entryID;
		notif->info.obj.ulObjType = MAPI_FOLDER;
		notif->info.obj.cbParentID = sizeof(O_ENTRYID);
		notif->info.obj.lpParentID = (LPENTRYID)(parent);
		
		//Henry 30.11.2005
		//Folders is not transacted objects so there is only modification notification
		//FIX ME if i'm wrong
		notif->ulEventType = fnevObjectModified;
		// Notify any listener interested that this folder has changed.
		if(!bRootFolder)
		{
			ULONG ulNotifyFlags = 0;
			hResult = msgstore->Notify(parent, 1, notif, &ulNotifyFlags);
		} // if
	} // if

	return hResult;
}

HRESULT O_IFolder::ProcessEvent( otlkcon_event_struct *ev )
{
    HRESULT result = S_OK;
	result = O_IContainer::ProcessEvent(ev);
    return result;
}
